package com.hrt.datasender.datacollect.drive;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.btcode.common.MyLog;
import com.btcode.exception.MsgException;
import com.btcode.log.ILog;
import com.btcode.reflect.AutoClassLoader;
import com.btcode.reflect.IClassFinder;
import com.hrt.datasender.datadao.model.BcDeviceConfigModel;
import com.hrt.datasender.datadao.model.BcDeviceMonitorParamModel;
import com.hrt.datasender.datadao.model.BcDevicesModel;
import com.hrt.datasender.datadao.service.BcDeviceConfigService;
import com.hrt.datasender.datadao.service.BcDeviceMonitorParamService;
import com.hrt.datasender.datadao.service.BcDeviceService;

/**
 * 驱动加载器
 * 
 * @author gislin
 *
 */
public final class DriveLoader implements IDriveLoader {

    private String basePacket = "com.hrt.datasender.datacollect.drive";

    private ILog log;

    private static volatile DriveLoader instance;

    /**
     * 已经被加载到系统中正在运行的驱动
     */
    private List<IDrive> loadedDrives = new ArrayList<>();

    /**
     * 程序中存在的驱动
     */
    private List<IDrive> existDrives = null;

    private DriveLoader() {
        log = MyLog.getInstance().getLogger(getClass().getName());
    }

    public static DriveLoader getInstance() {
        if (instance == null) {
            synchronized (DriveLoader.class) {
                if (instance == null) {
                    instance = new DriveLoader();
                }
            }
        }
        return instance;
    }

    public void resetDrive() {
        loadedDrives = null;
    }

    public List<IDrive> getExistDrives() {

        if (existDrives != null) {
            return existDrives;
        }

        try {
            IClassFinder driveFinder = new DriveFinder();
            AutoClassLoader driveClassLoader = new AutoClassLoader(driveFinder);

            List<IDrive> driveList = new ArrayList<>();
            List<Class<?>> driveClasses = driveClassLoader.loadClass(getClass(), basePacket);

            for (Class<?> myClass : driveClasses) {
                Object instance = myClass.newInstance();
                driveList.add((IDrive) instance);
                // driveList.add((SerialDrive) instance);
            }

            existDrives = driveList;

            return driveList;
        }
        catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IOException e) {
            log.error("获取当前存在的驱动出错", e);
            return null;
        }
    }

    /**
     * 加载驱动,并设置参数,加载后就直接启动了
     * 
     * @throws IOException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws ClassNotFoundException
     * @throws MessageException
     */
    public synchronized List<IDrive> loadAndGetDrive() throws Exception {

        if (loadedDrives != null && loadedDrives.size() > 0) {
            return loadedDrives;
        }

        List<IDrive> drivers = new ArrayList<>();

        List<BcDevicesModel> devices = BcDeviceService.getInstance().getDevices();
        List<IDrive> allDrivers = getExistDrives();

        for (BcDevicesModel device : devices) {
            String protocolId = device.getProtocolId();

            try {
                for (IDrive driver : allDrivers) {

                    if (driver.getDriveId().equals(protocolId)) {
                        List<BcDeviceMonitorParamModel> params = BcDeviceMonitorParamService.getInstance()
                                .getParamByDeviceId(device.getId());
                        List<BcDeviceConfigModel> bcDeviceConfigs = BcDeviceConfigService.getInstance()
                                .getList(device.getId());

                        /**
                         * 这里每次循环都要新实例化一个，因为一个驱动可以对应多个设备，而设备在通过驱动处理数据的时候，驱动会保持设备的数据。
                         * 这就要求每个设备都要对应一个自己的驱动实例。
                         */
                        IDrive driverInstance = driver.getClass().newInstance();
                        driverInstance.init(device, params, bcDeviceConfigs);
                        drivers.add(driverInstance);

                        log.info("驱动：" + basePacket + "." + protocolId + "已加载");
                    }
                }
            }
            catch (Exception e) {
                throw new MsgException("加载驱动：" + basePacket + "." + protocolId + "失败", e);
            }
        }

        loadedDrives = drivers;

        return drivers;
    }

}
